package com.ahdms.es.engine;

import com.ahdms.es.bean.SealInfo;
import com.ahdms.es.bean.SesSignInfo;
import com.ahdms.es.bean.enums.ApiCode;
import com.ahdms.es.exceptions.ApiException;
import com.ahdms.es.exceptions.InvalidSesSignFormatException;
import com.ahdms.es.exceptions.VerifySesSignException;
import com.ahdms.es.gm.v1.SES_Signature;
import com.ahdms.es.gm.v1.TBS_Sign;
import com.ahdms.es.result.VerifyResult;
import com.ahdms.es.util.AsnUtil;
import com.ahdms.es.util.DataConvert;
import com.ahdms.es.util.DateUtils;
import com.ahdms.es.util.ResultAssert;
import org.bouncycastle.asn1.*;
import org.bouncycastle.asn1.x509.Certificate;
import org.bouncycastle.asn1.x509.Time;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Optional;

/**
 * 国密格式的电子签章验证
 *
 * @author qinxiang
 * @date 2021-04-09 11:21
 */
public class SesSignatureGMVerifyEngine extends SesSignatureVerifyEngine {
    private static final Logger log = LoggerFactory.getLogger(SesSignatureGMVerifyEngine.class);

    private SesSignInfo sesSignInfo;

    private final Certificate signCert;

    private final SES_Signature sesSignature;

    public SesSignatureGMVerifyEngine(byte[] sesSignatureData) {
        this.sesSignature = SES_Signature.getInstance(sesSignatureData);
        this.signCert = Certificate.getInstance(this.sesSignature.getToSign().getCert().getOctets());
        this.sesSignInfo = new SesSignInfo(DateUtils.format(getSignDate()),DataConvert.toSealInfo(sesSignature.getToSign().getEseal()),DataConvert.certToCertInfo(this.signCert));
    }

    public SesSignatureGMVerifyEngine(SES_Signature sesSignature) {
        this.sesSignature = sesSignature;
        this.signCert = Certificate.getInstance(this.sesSignature.getToSign().getCert().getOctets());
    }

    public static SesSignatureGMVerifyEngine getInstance(byte[] sesSignatureData) throws InvalidSesSignFormatException {
        try {
            if (sesSignatureData != null) {
                return new SesSignatureGMVerifyEngine(sesSignatureData);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        throw new InvalidSesSignFormatException();
    }

    @Override
    public void verifySignData() throws IOException, VerifySesSignException {
        String signatureAlgorithmId = sesSignature.getToSign().getSignatureAlgorithm().getId();
        // TODO 前端的签名原文 有些是待签名数据的Base64值
        byte[] srcData = sesSignature.getToSign().getEncoded();
        byte[] signValue = sesSignature.getSignature().getOctets();

        verifySignData(signCert, signatureAlgorithmId, srcData, signValue);

    }

    @Override
    public void verifySeSeal() throws VerifySesSignException {
        VerifyResult<SealInfo> verify = SeSealVerifyEngine.verify(sesSignature.getToSign().getEseal());
        if(!verify.isResult()){
            log.error("电子印章验证失败..{}",verify.getErrorMsg());
            throw new VerifySesSignException(ApiCode.SESIGN_VERIFY_Seal_Fail);
        }
    }

    @Override
    public Certificate getSignCert() {
        return this.signCert;
    }

    @Override
    public Date getRevokeDate() {
        return revokeDate;
    }

    @Override
    public Date getSignDate() {
        ASN1BitString timeInfo = Optional.ofNullable(sesSignature)
                .map(SES_Signature::getToSign)
                .map(TBS_Sign::getTimeInfo)
                .orElseThrow(() -> new ApiException("签章时间解析失败"));
        Date date = dmsParseDate(timeInfo);
        if(date == null){
            date = jgParseDate(timeInfo);
        }
        if(date == null){
            throw new ApiException("签章时间解析失败");
        }
        return date;

    }

    private Date dmsParseDate(ASN1BitString timeInfo){
        try {
            Date date = new Time(AsnUtil.byteToASN1Primitive(timeInfo.getOctets())).getDate();
            return date;
        }catch (Exception e){

        }
        return null;
    }

    private Date jgParseDate(ASN1BitString timeInfo){
        try {
            Date parse = new SimpleDateFormat("yyMMddHHmmss'Z'").parse(new String(timeInfo.getOctets()));
            return parse;
        } catch (Exception e) {
//            e.printStackTrace();
        }
        return null;
    }

    @Override
    public byte[] getSesDataHash() {
        return sesSignature.getToSign().getDataHash().getOctets();
    }

    @Override
    public SesSignInfo getSesSignInfo(){
        return SesSignInfo.builder().signDate(DateUtils.format(getSignDate()))
                .sealInfo(DataConvert.toSealInfo(sesSignature.getToSign().getEseal()))
                .certInfo(DataConvert.certToCertInfo(this.signCert))
                .build();
    }
}
